# encoding: utf-8

class Task
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::CriteriaExt

  field :keyword,        :type => String

  field :current_page,   :type => Integer, :default => 1
  field :detail,         :type => Boolean
  field :sorttype,       :type => String
  field :msgtype,        :type => String
  field :starttime,      :type => String
  field :endtime,        :type => String
  field :longitue,       :type => String
  field :latitude,       :type => String
  field :radius,         :type => String
  field :searchtime,     :type => String,
                         :default => (Time.now - 1.hours).strftime('%Y-%m-%d-%H')
  field :province,       :type => String
  field :city,           :type => String
  field :issue,          :type => Hash, :default => {}
  field :issue_token,    :type => String
  field :is_filter,      :type => Boolean, :default => false
  field :filter_token,   :type => String
  field :spammer_id,     :type => String
  field :search_type,    :type => String
  field :favorite,       :type => Boolean
  field :tweets_location,:type => Array, :default => []
  field :filter_sources, :type => Array, :default => ['皮皮时光机','专业版微博','享拍微博通','分享按钮', '新浪机构认证']
  field :summary,        :type => Hash, :default => {}
  # field :sent_count, :type => Integer

  field :weibo_lists,    :type => Array, :default => []


  belongs_to  :account
  has_many    :push_histories

  has_many    :filtered_tasks, :class_name => 'Task', :inverse_of => :master_task
  belongs_to  :master_task,    :class_name => 'Task', :inverse_of => :filtered_tasks

  scope :today, ->(aid = nil){
    today_scope = where(:updated_at.gt => Date.today)
    today_scope = today_scope.where(:account_id => aid) if aid
    today_scope
  }

  scope :day, ->(aid = nil, date){
    today_scope = where(:updated_at.gt => Date.parse(date))
    today_scope = today_scope.where(:updated_at.lt => Date.parse(date).tomorrow)
    today_scope = today_scope.where(:account_id => aid) if aid
    today_scope
  }

  scope :favorite, ->{
    where(:favorite => true)
  }

  scope :no_filter, ->{ where(:is_filter => false)}
  scope :filtered, ->{where(:is_filter => true)}
  after_create :make_cache, :make_master

  WEIBO          = "1"
  QQ             = "2"
  MAX_SEND_COUNT = 60
  PER_PAGE       = 20

  # before_save :set_default_options

  # def set_default_options
  #   if self.endtime.nil? || self.endtime.empty?
  #     self.endtime = self.searchtime
  #   end
  # end
  # has_many :page

  # # # # # # # # # # # # # # # #
  #
  # 从这里开始是类方法
  #
  # # # # # # # # # # # # # # # #

  def self.get_task(keyword, account, task_id = "", issue = {})
    if task_id.empty?
      task = account.tasks.where(:keyword => keyword).first
    else
      task = account.tasks.where(:_id     => task_id).first
    end
    task
  end

  def self.pick_up(options = {})
    # 这里定义一下交互方式
    if options[:task].present?
      task = self.where(:_id => options[:task]).first
    elsif options[:keyword]
      task = self.where(:keyword => options[:keyword])
                 .where(:created_at.gt => (Time.now - 3.days))
                 .where(:is_filter => false)
                 .first
      if task && task.search_type.capitalize != options[:search_type]
        options[:is_filter] =true
      end
    else
      raise ">遇到错误: 缺少task_id或者keyword"
    end
    unless task
      task = self.create(:keyword => options[:keyword],
        :issue        => options[:issue],
        :issue_token  => Task.g_issue_token(options[:issue]),
        :sorttype     => options[:sorttype],
        :msgtype      => options[:msgtype],
        :search_type  => options[:search_type],
        :starttime    => options[:starttime],
        :endtime      => options[:endtime],
        :province     => options[:province],
        :city         => options[:city],
        :account_id   => self.criteria.selector["account_id"] #这个用法相当危险
      )
    end
    # task.update_search_type options[:search_type]
    task.update_current_page options[:current_page]
    if options[:is_filter].to_s == 'true'
      task = task.filter_results(options)
    end
    task
  end

  def self.g_issue_token(issue)
    Base64.strict_encode64(issue.to_s) if issue
  end

  # # # # # # # # # # # # # # # #
  #
  # 从这里开始是对外开放的接口
  #
  # # # # # # # # # # # # # # # #

  def search
    # 检查页面缓存
    if @page = Utility::Page.where(:page_token => "#{self._id}_#{search_type.downcase}_p#{self.current_page}").first
      logger.info ">查询状态: 从缓存读取第#{self.current_page}页"
      logger.info "========== 查询完毕 =========="
      return @page.present_show
    else
      logger.info ">查询状态: 该页无缓存, 将从#{search_type} 获取结果"
      get_summary :make_page
      @page = Utility::Page.make_page(self)
    end
    case self.search_type
    when :Weibo
      get_weibos
    when :Tencent
      get_tencent
    end
    @page.present_show
  end

  def page_destroy
    logger.info ">筛选开始:清除搜索缓存"
    Utility::Page.where(:page_token => /#{self._id}_/).destroy_all
  end

  def filter_results(options)
    logger.info ">筛选开始:设置筛选条件"
    _filter_token = Digest::MD5.hexdigest(
      options[:keyword].to_s       +
      options[:sorttype].to_s      +
      options[:msgtype].to_s       +
      options[:search_type].to_s   +
      options[:starttime].to_s     +
      options[:endtime].to_s       +
      options[:province].to_s      +
      options[:city].to_s
      )
    unless task = Task.where(:filter_token => _filter_token).first
      task = Task.create(
        :keyword      => options[:keyword],
        :sorttype     => options[:sorttype],
        :msgtype      => options[:msgtype],
        :search_type  => options[:search_type],
        :starttime    => options[:starttime],
        :endtime      => options[:endtime],
        :province     => options[:province],
        :city         => options[:city],
        :is_filter    => true,
        :filter_token => _filter_token,
        :account      => account,
        :master_task  => self.master_task || self
      )
    end
    task
  end

  def clear_unclicked
    tweets = all_tweets(:unclicked => true)
    Utility::BasicTweet.destroy_comment_batch(tweets.map(&:_id))
  end

  def clear_failed
    tweets = all_tweets(:all => true).where(:error.ne => nil)
    Statistics::Tweet.where(:_id.in => tweets.map(&:_id)).destroy_all
  end

  def update_current_page(current_page)
    self.current_page = current_page
    save
  end

  def update_search_type(search_type)
    self.search_type = search_type
    save
  end

  def search_type
    self["search_type"].to_sym
  end

  def add_push_histories(attrs ={})
    self.push_histories.create(:comment => attrs[:comment], :tweets_list => attrs[:tweets_list])
  end

  def new_comment_job(tweets, comment)
    ph = add_push_histories :comment => comment, :tweets_list => tweets

    _pages = Utility::Page
      .where(:page_token => /#{self._id}_/)
      .order_by(:result_page.asc)
      .select do |page|
        !page[:page_token].match(/cache/)
      end
    _pages.each_with_index do |_page, _index|
      _tweets = tweets.map{|t| t[1]}.select{|a| a['pindex'] == (_index + 1).to_s}
      _tweets.each do |_tweet|
        _page.tweets[_tweet['index'].to_i]['commented'] = "正在发送"
      end
      _page.selected = _tweets
      _page.save
    end

    Sidekiq.redis do |redis|
      c_flag = true if redis.llen("flowy:create_comment:#{search_type}:#{ph._id}") > 0
      tweets.each_value do |tweet|
        redis.lpush "flowy:create_comment:#{search_type}:#{ph._id}", tweet.to_json #"#{tweet['mid']},#{tweet['uid']}"
      end
      CommentsWorker.perform_async(ph._id, comment, search_type) unless c_flag
    end
    true
  end

  def recomment_job(tweets)
    tweets['0'] ? _wid = tweets['0']['wid'] : return
    _pages = Utility::Page
      .where(:page_token => /#{self._id}_/)
      .order_by(:result_page.asc)
      .select do |page|
        !page[:page_token].match(/cache/)
      end
    _pages.each_with_index do |_page, _index|
      _tweets = tweets.map{|t| t[1]}.select{|a| a['pindex'] == (_index + 1).to_s}
      _tweets.each do |_tweet|
        _page.tweets[_tweet['index'].to_i]['commented'] = "正在重发"
      end
      _page.selected = _tweets
      _page.save
    end
    p _wid
    if _f_tweet = Statistics::Tweet.where('for_t.wid' => _wid).first
      ph = _f_tweet.push_history
    end

    Sidekiq.redis do |redis|
      c_flag = true if redis.llen("flowy:create_comment:#{search_type}:#{ph._id}") > 0
      tweets.each_value do |tweet|
        redis.lpush "flowy:create_comment:#{search_type}:#{ph._id}", tweet.to_json #"#{tweet['mid']},#{tweet['uid']}"
      end
      CommentsWorker.perform_async(ph._id, "", search_type) unless c_flag
    end
    true
  end

  # 这个是为了staging而写的方法

  def sent_count
    push_histories.map{|ph| ph.tweets.count}.inject(&:+) || 0
  end

  def clicked_count
    all_tweets(:all => true).map{|_t| _t.clicks.count}.inject(&:+)
  end

  def present_list
    present_attrs :created_at, :keyword, :_id
    present_add   :sent_count    => sent_count
    present_add   :clicked_count => clicked_count
    present_add   :push_histories => push_histories.map{|ph| ph}
  end

  def present_show
    present_attrs :_id,
                  :keyword,
                  :detail,
                  :sorttype,
                  :msgtype,
                  :starttime,
                  :endtime,
                  :longitue,
                  :latitude,
                  :radius,
                  :searchtime,
                  :province,
                  :city,
                  :is_filter,
                  :search_type

    present_add :current_page => self.current_page || [(Utility::Page.where(:page_token => /#{self._id}_/).count - 1), 1].max

    present_add :pages => pages
  end

  def pages
    Utility::Page
      .where(:page_token => /#{self._id}_/)
      .order_by(:result_page.asc)
      .present_json(:show)
      .select do |page|
        !page[:page_token].match(/cache/)
      end
  end

  def all_tweets(options)
    if options[:self]
      ph_ids = self.push_history_ids
    else
      ph_ids = Task.where(:master_task_id => master_task_id).map(&:push_history_ids).inject(&:+)
    end
    tweets = Statistics::Tweet.where(:push_history_id.in => ph_ids)
                  # .order_by(:created_at, :desc)
    if is_filter
      tweets = tweets.where(:sns_type => search_type)
    end
    return tweets if options[:all]

    if options[:unclicked]
      tweets = tweets.where(:clicks => {})
    end

    if options[:self]
      all_tweets(:all => true).map(&:location).uniq
    end

    if options[:location].present?
      tweets = tweets.where(:location => options[:location])
    end
    if options[:time].present?
      tweets = tweets.where(:created_at.gt => options[:time])
    end
    if options[:page].present?
      tweets = tweets.page(options[:page])
                     .per(50)
    end
    tweets
  end

  def refresh_tweets_location
    master_task.tweets_location = all_tweets(:all => true).map(&:location).uniq
    master_task.save
  end

  def tweets_location
    master_task[:tweets_location]
  end

  def present_tweets(options)
    present_attrs :tweets_location
    present_add   :tweets => all_tweets(options)
  end

  def get_summary(make = false)
    _summary   = self.summary

    case make
    when :make_now
      _provinces = {}
      _cities    = {}
      # _pages_count = Utility::Page
      #   .where(:page_token => /#{self._id}_/)
      #   .count

      _tweets_count = Statistics::Tweet.where(:push_history_id.in => self.push_history_ids).count
      _s_tweets_count = Statistics::Tweet.success.where(:push_history_id.in => self.push_history_ids).count

      _location = all_tweets(:self => true).map(&:location).uniq

      _location.each do |loc|
        loc = loc.split(' ')
        if loc[0]
          _provinces[loc[0]] ||= 0
          _provinces[loc[0]] += 1
        end
        if loc[1]
          _cities[loc[1]] ||= 0
          _cities[loc[1]] += 1
        end
      end
      _cities_smr = (_cities.sort_by { |name, count| count })[0..5].map{|c| "#{c[0]} #{c[1]}条"}.join(',')

      _summary[:cities]   = _cities
      _summary[:provinces]= _provinces
      _summary[:location] = _location

      # _summary[:total]    = "再以关键字为\"#{self.keyword}\"的搜索中, 总共查看前 #{(_pages_count -1) * 20} 条结果."
      _summary[:comment]  = "选择并且发送了#{_tweets_count}条微博, 成功发送的有#{_s_tweets_count}条."
      _summary[:location] = "他们来自全球#{_location.count}个地区, #{_cities.count}个城市, 其中来源最多的是几个城市分别是#{_cities_smr}. "
      _summary[:time]     = "其中最早的来自于,最晚的来自于,时间上跨越了."
    when :make_page
      _pages_count = Utility::Page
        .where(:page_token => /#{self._id}_/)
        .count
      _summary[:total]    = "在以关键字为\"#{self.keyword}\"的搜索中, 总共查看了前 #{_pages_count * 20} 条结果."
    else
      return self.summary
    end

    self.summary = _summary
    save
    _summary
  end

  private

  def spammer(another = false)
    # 这里需要重写
    if another || !@spammer = Utility.const_get(search_type).active.where(_id: self.spammer_id).first
      @spammer = Utility.const_get(search_type).active[rand(Utility.const_get(search_type).active.count)]
      self.spammer_id = @spammer._id if @spammer
      save
    end
    if @spammer
      return @spammer
    else
      raise "没有可用账号了!"
    end
  end

  def get_weibos
    spammer.search(@page, search_options)
    _retry = 1
    while @page.error == Utility::Weibo::ERROR_RESCUE[:another_retry] && _retry < 3
      _retry += 1
      spammer(true).search(@page, search_options)
    end
    @page.make_cache
  end

  def get_tencent
    spammer.search(@page, search_options)
    @page.make_cache
  end

  def search_options
    @options = {
      :keyword      => keyword,
      :page         => result_page,
      :per_page     => Task::PER_PAGE,
      :detail       => detail,
      :sorttype     => sorttype,
      :search_type  => search_type,
      :searchtime   => searchtime,
      :msgtype      => msgtype,
      :search_type  => search_type,
      :starttime    => starttime,
      :endtime      => endtime,
      :province     => province,
      :city         => city,
      :filter_sources => filter_sources
    }
  end

  def result_page
    case search_type
    when :Weibo
      if _page_cache = Utility::Page.where(:page_token => "#{self._id}_weibo_cache").first
        _page_cache.result_page
      else
        make_cache.result_page
      end
    when :Tencent
      current_page
    end
  end

  def make_cache
    Utility::Page.find_or_create_by(:page_token => "#{_id}_#{search_type.downcase}_cache")
  end

  def make_master
    unless is_filter
      self.master_task = self
      save
    end
  end

end
